﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.IO;
using System.Windows.Forms;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;
using ZoneFiveSoftware.Common.Visuals;
using ZoneFiveSoftware.Common.Visuals.Fitness;
using ZoneFiveSoftware.Common.Visuals.Util;
using ZoneFiveSoftware.Common.Visuals.Chart;
using STCommon.Controller;
using STCommon.Data;
using STCommon.Resources;
using STCommon.View;
using DistributionPlugin.Controller;
using DistributionPlugin.Data;

namespace DistributionPlugin.View
{
    partial class DistributionDetailsControl : UserControl, ILabelUnitProvider
    {
        public DistributionDetailsControl(IDetailPage detailPage,
                                          IDailyActivityView activityDetailsView,
                                          ActivityDetailsOptions optionsInstance)
        {
            Debug.Assert(optionsInstance != null);

            // Data setup
            m_ActivityDetailsView = activityDetailsView;
            m_ActivityDetailsView.SelectionProvider.SelectedItemsChanged += new EventHandler(ActivityDetailsView_SelectedActivityChanged);

            m_OptionsInstance = optionsInstance;
            m_OptionsInstance.OptionChanged += new PropertyChangedEventHandler(OnOptionChanged);

            //UI callbacks & setup
            InitializeComponent();

            DistributionSetupControl.LabelProvider = this;
            DistributionSetupControl.Options = m_OptionsInstance;

            int defaultColumnWidth = DistributionTreeList.Size.Width / 5;
            DistributionTreeList.Columns.Add(new TreeList.Column("Range", CommonResources.Text.LabelRange, defaultColumnWidth, StringAlignment.Near));
            DistributionTreeList.Columns.Add(new TreeList.Column("TimePercent", TextResourceManager.TimePercentText, defaultColumnWidth, StringAlignment.Center));
            DistributionTreeList.Columns.Add(new TreeList.Column("DistancePercent", TextResourceManager.DistancePercentText, defaultColumnWidth, StringAlignment.Center));
            DistributionTreeList.Columns.Add(new TreeList.Column("TotalTime", CommonResources.Text.LabelTotalTime, defaultColumnWidth, StringAlignment.Center));
            DistributionTreeList.Columns.Add(new TreeList.Column("TotalDistance", CommonResources.Text.LabelTotalDistance, defaultColumnWidth, StringAlignment.Center));
            DistributionTreeList.NumLockedColumns = 1;
            DistributionTreeList.LabelProvider = new DistributionLabelProvider(this);
            m_OptionsInstance.TreeListSerializer.TreeList = DistributionTreeList;

            ChartSplitContainer.SplitterDistance = m_OptionsInstance.ChartSplitSize;

            MainLineChartWithButtons.LabelProvider = this;

            ChartHeaderActionBanner.SetMaximizable(detailPage);
        
            // Fake events to refresh UI & variables content
            UICultureChanged(PluginMain.GetApplication().SystemPreferences.UICulture);
            OnOptionChanged(null, null);
            ActivityDetailsView_SelectedActivityChanged(null, null);
        }

        private void ActivityDetailsView_SelectedActivityChanged(object sender, EventArgs e)
        {
            SelectedActivities = Utils.GetAllContainedUniqueItemsOfType<IActivity>(m_ActivityDetailsView.SelectionProvider.SelectedItems);

            // Refresh options speed referential
            m_OptionsInstance.SpeedReferential = SpeedUnit;

            // Refresh UI that depends on activity selection
            this.Enabled = SelectedActivities.Count > 0;

            DistributionSetupControl.Activities = SelectedActivities;

            if (DetailPageVisible)
            {
                UpdateDistributionChart();
            }
        }

        void OnActivityDataChanged(object sender, PropertyChangedEventArgs e)
        {
            if (DetailPageVisible)
            {
                // Fake an update of selected activities.  This refreshes everything.
                ActivityDetailsView_SelectedActivityChanged(this, null);
            }
        }

        private void OnOptionChanged(object sender, PropertyChangedEventArgs e)
        {

            MainLineChartWithButtons.Referential = m_OptionsInstance.SelectedReferential;

            if (DetailPageVisible)
            {
                UpdateDistributionChart();
            }
        }

        private void OnCopyDistributionToClipboard(object sender, EventArgs e)
        {
            DistributionTreeList.CopyTextToClipboard(true, "\t");
        }

        private void OnExportDistributionToCSV(object sender, EventArgs e)
        {
            SaveFileDialog dlg = new SaveFileDialog();
            string filter = STCommon.Resources.TextResourceManager.CSVFileText + " (*.csv)|*.csv";

            dlg.Filter = filter;

            if (dlg.ShowDialog() == DialogResult.OK)
            {
                Stream fileStream = dlg.OpenFile();

                DistributionTreeList.ExportToCSV(fileStream);

                fileStream.Close();
            }
        }

#region ILabelUnitProvider Members

        public Enums.SportTracksDataTrackTypes DataType
        {
            get { return m_OptionsInstance.SelectedDataType; }
        }


        public Length.Units DistanceUnit
        {
            get
            {
                Length.Units distanceUnit;
                Speed.Units speedUnit;

                Utils.GetCommonDistanceUnitForActivities(SelectedActivities, PluginMain.GetApplication(),
                                                         out distanceUnit, out speedUnit);

                return distanceUnit;
            }
        }

        public Speed.Units SpeedUnit
        {
            get
            {
                Length.Units distanceUnit;
                Speed.Units speedUnit;

                Utils.GetCommonDistanceUnitForActivities(SelectedActivities, PluginMain.GetApplication(),
                                                         out distanceUnit, out speedUnit);

                return speedUnit;
            }
        }

        public String GetUnitsTextForSelection()
        {
            switch (m_OptionsInstance.SelectedDataType)
            {
                case Enums.SportTracksDataTrackTypes.Cadence:
                    {
                        return CommonResources.Text.LabelRPM;
                    }
                case Enums.SportTracksDataTrackTypes.Elevation:
                    {
                        return Length.LabelPlural(Utils.GetCommonElevationUnitForActivities(SelectedActivities,
                                                                                            PluginMain.GetApplication()));
                    }
                case Enums.SportTracksDataTrackTypes.Grade:
                    {
                        return CommonResources.Text.LabelPercent;
                    }
                case Enums.SportTracksDataTrackTypes.HeartRate:
                    {
                        return String.Empty;
                    }
                case Enums.SportTracksDataTrackTypes.Speed:
                    {
                        return Speed.Label(SpeedUnit, new Length(1, DistanceUnit));
                    }
                case Enums.SportTracksDataTrackTypes.Power:
                    {
                        return CommonResources.Text.LabelWatts;
                    }
                default:
                    {
                        return String.Empty;
                    }
            }
        }

        public string GetSpeedLabelForSelection()
        {
            if (SpeedUnit == Speed.Units.Speed)
            {
                return CommonResources.Text.LabelSpeed;
            }
            else
            {
                return CommonResources.Text.LabelPace;
            }
        }

        public string GetIntervalTextForSelection()
        {
            if (m_OptionsInstance.SelectedDataType == Enums.SportTracksDataTrackTypes.Speed &&
                SpeedUnit == Speed.Units.Pace)
            {
                return Utils.TimeMinutesToString(m_OptionsInstance.SelectedDataTypeIntervalLength);
            }
            else
            {
                return String.Format("{0:0.##}", Math.Round(m_OptionsInstance.SelectedDataTypeIntervalLength, 2));
            }
        }

#endregion

#region UI Callbacks

        private void DistributionSetupControl_SizeChanged(object sender, EventArgs e)
        {
            // AutoSize is 1 refresh late so this patch fixes it.  Bug might be elsewhere,
            //  I just can't seem to find it
            DistributionSetupControl.Size = new Size(DistributionSetupControl.Width,
                                                     DistributionSetupControl.Height);
        }

        private void DistributionDetailsControl_Resize(object sender, EventArgs e)
        {
            ChartSplitContainer.SplitterDistance = m_OptionsInstance.ChartSplitSize;
        }

        private void ChartHeaderActionBanner_MenuClicked(object sender, EventArgs e)
        {
            ContextMenu menu = new ContextMenu();
            MenuItem menuItem;

            // Distance
            menuItem = new IconizedSTMenuItem(PluginMain.GetApplication().VisualTheme,
                                              CommonResources.Text.LabelDistance,
                                              new EventHandler(DistanceMenuEventHandler));
            menuItem.Checked = (m_OptionsInstance.SelectedReferential == Enums.SportTracksXAxisReferential.Distance);
            menu.MenuItems.Add(menuItem);

            // Time
            menuItem = new IconizedSTMenuItem(PluginMain.GetApplication().VisualTheme,
                                              CommonResources.Text.LabelTime,
                                              new EventHandler(TimeMenuEventHandler));
            menuItem.Checked = (m_OptionsInstance.SelectedReferential == Enums.SportTracksXAxisReferential.Time);
            menu.MenuItems.Add(menuItem);

            menu.Show(this, PointToClient(MousePosition));
        }

        public void DistanceMenuEventHandler(object sender, EventArgs args)
        {
            m_OptionsInstance.SelectedReferential = Enums.SportTracksXAxisReferential.Distance;
        }

        public void TimeMenuEventHandler(object sender, EventArgs args)
        {
            m_OptionsInstance.SelectedReferential = Enums.SportTracksXAxisReferential.Time;
        }

        private void ChartSplitContainer_SplitterMoving(object sender, SplitterCancelEventArgs e)
        {
            m_OptionsInstance.ChartSplitSize = ChartSplitContainer.SplitterDistance;
        }

        private void DistributionTreeList_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right &&
                SelectedActivities.Count > 0)
            {
                ContextMenu menu = new ContextMenu();
                IconizedSTMenuItem menuItem;

                menuItem = new IconizedSTMenuItem(PluginMain.GetApplication().VisualTheme,
                                                  CommonResources.Text.ActionCopy,
                                                  new EventHandler(OnCopyDistributionToClipboard));
                menu.MenuItems.Add(menuItem);
                menuItem = new IconizedSTMenuItem(PluginMain.GetApplication().VisualTheme,
                                                  CommonResources.Text.ActionExport,
                                                  new EventHandler(OnExportDistributionToCSV),
                                                  CommonResources.Images.Export16,
                                                  null);
                menu.MenuItems.Add(menuItem);

                menu.Show(this, PointToClient(MousePosition));
            }
        }

        private void DistributionTreeList_SelectedItemsChanged(object sender, EventArgs e)
        {
            IList selection = DistributionTreeList.SelectedItems;

            MainLineChartWithButtons.SelectedData = selection;

            if (SelectedActivities.Count == 1 &&
                SelectedActivities[0].GPSRoute != null)
            {
                DistributionZoneInfo allSelectionZoneInfo = new DistributionZoneInfo(null);

                foreach (DistributionZoneInfo zoneInfo in selection)
                {
                    allSelectionZoneInfo += zoneInfo;
                }

                DistributionTrackSelectionInfo mapSelection = new DistributionTrackSelectionInfo(SelectedActivities[0],
                                                                                                 allSelectionZoneInfo);
                m_ActivityDetailsView.RouteSelectionProvider.SelectedItems = new IItemTrackSelectionInfo[] { mapSelection };
            }
        }

        private void MainLineChartWithButtons_DistributionBarClicked(IList<DistributionZoneInfo> zonesInfo)
        {
            IList selection = new List<object>();

            foreach (DistributionZoneInfo zoneInfo in zonesInfo)
            {
                selection.Add(zoneInfo);
            }

            DistributionTreeList.SelectedItems = selection;

            // Display first selected item
            if (zonesInfo.Count > 0)
            {
                DistributionTreeList.EnsureVisible(zonesInfo[0]);
            }
        }

#endregion

        public void ThemeChanged(ITheme visualTheme)
        {
            DistributionTreeList.ThemeChanged(visualTheme);
            MainLineChartWithButtons.ThemeChaged(visualTheme);
            ChartHeaderActionBanner.ThemeChanged(visualTheme);
        }

        public void UICultureChanged(CultureInfo culture)
        {
            DistributionSetupControl.UICultureChanged(culture);
        }

        private void UnregisterActivityDataChangedHelpers()
        {
            foreach (ActivityDataChangedHelper helper in m_ActivityChangedHelpers)
            {
                helper.PropertyChanged -= new PropertyChangedEventHandler(OnActivityDataChanged);
            }

            m_ActivityChangedHelpers.Clear();
        }

        private void RegisterActivityDataChangedHelpers()
        {
            foreach (IActivity activity in SelectedActivities)
            {
                ActivityDataChangedHelper newHelper = new ActivityDataChangedHelper(PluginMain.GetApplication(), activity);

                newHelper.PropertyChanged += new PropertyChangedEventHandler(OnActivityDataChanged);
                m_ActivityChangedHelpers.Add(newHelper);
            }
        }

        private void UpdateDistributionChart()
        {
            if (!m_bUpdatingChart)
            {
                m_bUpdatingChart = true;

                if (DetailPageVisible)
                {
                    IDictionary<INamedLowHighZone, DistributionZoneInfo> distributionResults = new Dictionary<INamedLowHighZone, DistributionZoneInfo>();

                    // Don't process when no activities selected
                    if (SelectedActivities != null &&
                        SelectedActivities.Count > 0)
                    {
                        IZoneCategory tempZoneCategory = Utils.CreateUnregisteredZoneCategory(PluginMain.GetApplication().Logbook);

                        DataProcessing.FillZoneCategoryForActivitiesAndOptionsHelper(SelectedActivities,
                                                                                     m_OptionsInstance,
                                                                                     tempZoneCategory);
                        distributionResults = DataProcessing.GetCumulativeInfoForActivities(SelectedActivities,
                                                                                            m_OptionsInstance,
                                                                                            tempZoneCategory);

                        DistributionTreeList.RowData = distributionResults.Values;
                        MainLineChartWithButtons.DistributionData = distributionResults.Values;
                    }
                    else
                    {
                        DistributionTreeList.RowData = null;
                        MainLineChartWithButtons.DistributionData = null;
                    }
                }

                m_bUpdatingChart = false;
            }
        }

        private IList<IActivity> SelectedActivities
        {
            get { return m_SelectedActivities; }
            set
            {
                if (SelectedActivities != value)
                {
                    UnregisterActivityDataChangedHelpers();

                    m_SelectedActivities = value;

                    RegisterActivityDataChangedHelpers();
                }
            }
        }

        public bool DetailPageVisible
        {
            get { return m_DetailPageVisible; }
            set
            {
                if (DetailPageVisible != value)
                {
                    m_DetailPageVisible = value;

                    if (DetailPageVisible)
                    {
                        UpdateDistributionChart();
                    }
                }
            }
        }

        private IDailyActivityView m_ActivityDetailsView = null;
        private IList<IActivity> m_SelectedActivities = null;
        private List<ActivityDataChangedHelper> m_ActivityChangedHelpers = new List<ActivityDataChangedHelper>();
        private ActivityDetailsOptions m_OptionsInstance = null;
        private bool m_DetailPageVisible = false;
        private bool m_bUpdatingChart = false;

    }
}
